package com.cmy.framework_network.apiresponse

import com.cmy.framework_network.beans.BaseResponse
import com.cmy.framework_network.error.BusinessException
import com.cmy.framework_network.error.FailureHandler
import com.cmy.framework_network.utils.MoshiUtils
import okhttp3.Request
import okio.Timeout
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.io.IOException
import java.lang.reflect.Type

class NetworkResponseCall<S : Any> (private val delegate: Call<S>,
                                    private val errorConverter: FailureHandler?):
    Call<NetworkResponse<S>> {
    override fun enqueue(callback: Callback<NetworkResponse<S>>) {
            return delegate.enqueue(object : Callback<S>{
                override fun onResponse(call: Call<S>, response: Response<S>) {
                    val body = response.body()
                    val code = response.code()
                    val error = response.errorBody()

                    if (response.isSuccessful){
                        if (body != null){
                            callback.onResponse(this@NetworkResponseCall, Response.success(NetworkResponse.Success(body)))
                        }else{

                            callback.onResponse(this@NetworkResponseCall, Response.success(NetworkResponse.UnknownError(null)))
                        }
                    }else{
                        if (error != null && error.contentLength() > 0){
                            // 500X
                            val errorResponse =
                                MoshiUtils.fromJson<BaseResponse>(
                                    error.string(),
                                    BaseResponse::class.java
                                )
                            errorConverter?.onFailure(
                                BusinessException(
                                    errorResponse?.code ?: -1,
                                    errorResponse?.msg ?: ""
                                )
                            )
                            callback.onResponse(
                                this@NetworkResponseCall,
                                Response.success(
                                    NetworkResponse.ApiError(
                                        errorResponse?.msg ?: "",
                                        errorResponse?.code ?: -1
                                    )
                                )
                            )

                        }else{
                            callback.onResponse(
                                this@NetworkResponseCall,
                                Response.success(NetworkResponse.NetworkError(error?.string()?:"Message is empty.", code))
                            )
                        }
                    }


                }

                override fun onFailure(call: Call<S>, throwable: Throwable) {
                    val networkResponse = when (throwable) {
                        is IOException -> NetworkResponse.NetworkError(throwable.message.toString(), 400)
                        is BusinessException -> {
                            errorConverter?.onFailure(throwable)
                            NetworkResponse.ApiError(throwable.message ?: "", throwable.code)
                        }
                        else -> NetworkResponse.UnknownError(throwable)
                    }

                    callback.onResponse(
                        this@NetworkResponseCall,
                        Response.success(networkResponse)
                    )
                }

            })
    }


    override fun isExecuted() = delegate.isExecuted

    override fun clone() = NetworkResponseCall(delegate.clone(), errorConverter)

    override fun isCanceled() = delegate.isCanceled

    override fun cancel() = delegate.cancel()

    override fun execute(): Response<NetworkResponse<S>> {
        throw UnsupportedOperationException("NetworkResponseCall doesn't support execute")
    }



    override fun request(): Request = delegate.request()

    override fun timeout(): Timeout = delegate.timeout()
}